// node reflect_demo.js
// npx babel-node reflect_demo.js

let _ = '';
const {log} = console;

let test01 = function(){
    let myObject = {
        foo: 1,
        bar: 2,
        get baz() {
            return this.foo + this.bar;
        },
    };

    _ = Reflect.get(myObject, 'foo')
    log(_)

    _ = Reflect.get(myObject, 'bar')
    log(_)

    _ = Reflect.get(myObject, 'baz')
    log(_)
};

// test01();

let teste02 = function(){
    let myObject = {
        foo: 1,
        bar: 2,
        get baz() {
            return this.foo + this.bar;
        },
    };

    let myReceiverObject = {
        foo: 4,
        bar: 5,
        baz: 6
    };

    _ = Reflect.get(myObject, 'baz', myReceiverObject);
    log(_)
};

// teste02();

let test03 = function(){
    let myObject = {
        foo: 1,
        set bar(value) {
            return this.foo = value;
        },
    };

    _ = myObject.foo
    log(_)

    Reflect.set(myObject, 'foo', 2);
    _ = myObject.foo
    log(_)

    Reflect.set(myObject, 'bar', 3);
    _ = myObject.foo
    log(_)
};
// test03();

let test04 = function(){
    let myObject = {
        foo: 4,
        set bar(value) {
            return this.foo = value;
        },
    };

    let myReceiverObject = {
        foo: 0,
    };

    Reflect.set(myObject, 'bar', 1, myReceiverObject);

    _ = myObject.foo;
    log(_);

    _ = myReceiverObject.foo;
    log(_);
};
// test04();

let test05 = function(){
    let p = {
        a: 'a'
    };

    let handler = {
        set(target, key, value, receiver) {
            log('set');
            Reflect.set(target, key, value, receiver);
        },
        defineProperty(target, key, attribute) {
            log('defineProperty');
            Reflect.defineProperty(target, key, attribute);
        }
    };

    let obj = new Proxy(p, handler);
    obj.a = 'A';
};
// test05();

// 如果Reflect.set没有传入receiver，那么就不会触发defineProperty拦截。

let test06 = function(){
    let p = {
        a: 'a'
    };

    let handler = {
        set(target, key, value, receiver) {
            log('set');
            Reflect.set(target, key, value);
        },
        defineProperty(target, key, attribute) {
            log('defineProperty');
            Reflect.defineProperty(target, key, attribute);
        }
    };

    let obj = new Proxy(p, handler);
    obj.a = 'A';
};
// test06();

// Reflect.has(obj, name)

let test07 = function(){
    let myObject = {
        foo: 1,
    };

    // 旧写法
    _ = 'foo' in myObject;
    log(_)

    // 新写法
    _ = Reflect.has(myObject, 'foo')
    log(_)
};
// test07();

// Reflect.deleteProperty(obj, name)

let test08 = function(){
    const myObj = { foo: 'bar' };
    log(myObj.foo);

    // 旧写法
    // delete myObj.foo;

    // 新写法
    Reflect.deleteProperty(myObj, 'foo');
    log(myObj.foo);
};
// test08();

// Reflect.construct(target, args)

let test09 = function(){
    let Greeting = function(name){
        this.name = name;
    };

    // new 的写法
    const instance = new Greeting('张三');
    log(instance.name)

    // Reflect.construct 的写法
    const instance2 = Reflect.construct(Greeting, ['李四']);
    log(instance2.name);
};
// test09();

// Reflect.getPrototypeOf(obj)

let test10 = function(){
    let FancyThing = function(){};

    const myObj = new FancyThing();

    // 旧写法
    _ = Object.getPrototypeOf(myObj) === FancyThing.prototype;
    log(_)

    // 新写法
    _ = Reflect.getPrototypeOf(myObj) === FancyThing.prototype;
    log(_)
};
// test10();

// Reflect.setPrototypeOf(obj, newProto)

let test11 = function(){
    const myObj = {};

    // 旧写法
    // Object.setPrototypeOf(myObj, Array.prototype);

    // 新写法
    Reflect.setPrototypeOf(myObj, Array.prototype);

    _ = myObj.length;
    log(_)
};
// test11();

let test12 = function(){
    _ = Reflect.setPrototypeOf({}, null);
    log(_)

    _ = Reflect.setPrototypeOf(Object.freeze({}), null);
    log(_)
};
// test12();

// Reflect.apply(func, thisArg, args)

let test13 = function(){
    const ages = [11, 33, 12, 54, 18, 96];

    // 旧写法
    {
        const youngest = Math.min.apply(Math, ages);
        const oldest = Math.max.apply(Math, ages);
        const type = Object.prototype.toString.call(youngest);
        log(youngest, oldest, type);
    }

    // 新写法
    {
        const youngest = Reflect.apply(Math.min, Math, ages);
        const oldest = Reflect.apply(Math.max, Math, ages);
        const type = Reflect.apply(Object.prototype.toString, youngest, []);
        log(youngest, oldest, type);
    }
};
// test13();

let test14 = function(){
    let myObject = {
        foo: 1,
        bar: 2,
        [Symbol.for('baz')]: 3,
        [Symbol.for('bing')]: 4,
    };

    _ = Object.getOwnPropertyNames(myObject);
    log(_)

    _ = Object.getOwnPropertySymbols(myObject);
    log(_)

    _ = Reflect.ownKeys(myObject);
    log(_)
};
// test14();

// 实例：使用 Proxy 实现观察者模式

let test15 = function(){
    const queuedObservers = new Set();

    const observe = fn => queuedObservers.add(fn);
    let set = function(target, key, value, receiver){
        const result = Reflect.set(target, key, value, receiver);
        queuedObservers.forEach(observe => observe());
        return result;
    };
    const observable = obj => new Proxy(obj, {set});


    const person = observable({
        name: '张三',
        age: 20
    });
    log(person);

    let print = function(){
        log(`${person.name}, ${person.age}`);
    };

    observe(print);
    person.name = '李四';
};

test15();



